package top.jacktgq.wait_notify.guardedSuspension.v3;

import java.util.Hashtable;
import java.util.Map;
import java.util.Set;

import top.jacktgq.utils.LogUtils;
import top.jacktgq.utils.Sleeper;


/**
 * 
 * @Title: GuardedObject.java
 * @Package top.jacktgq.wait_notify.guardedSuspension.v1
 * @Description: 同步模式之保护性暂停：用在一个线程等待另一个线程的执行结果
 * 					增加超时效果： 超过等待时间就停止等待，继续执行线程
 * @author CandyWall
 * @date 2020年11月1日 下午6:04:23
 * @version V1.0
 */
public class GuardedSuspensionDemo {
	public static void main(String[] args) {
		for(int i = 0; i < 3; i++) {
			new People();
		}
		Sleeper.sleep(1);
		for(int id : MailBoxes.getIds()) {
			new Postman(id, "邮件：" + id);
		}
	}
	
}

//邮递员
class Postman extends Thread {
	private int id;	//邮件编号
	private String mail;
	public Postman(int id, String mail) {
		this.id = id;
		this.mail = mail;
		start();
	}
	
	@Override
	public void run() {
		GuardedObject go = MailBoxes.getGuardedObject(id);
		LogUtils.log("Postman", "送信 id：" + go.getId());
		go.complete(mail);
	}
}

//居民
class People extends Thread {
	public People() {
		start();
	}
	@Override
	public void run() {
		GuardedObject go = MailBoxes.createGuardedObject();
		LogUtils.log("People", "开始收信 id：" + go.getId());
		
		Object mail = go.get(5000);
		LogUtils.log("People", "收到信 id：" + go.getId() + "，内容：" + mail);
	}
}

//小区的邮箱柜
class MailBoxes {
	private static Map<Integer, GuardedObject> boxes = new Hashtable<>();
	private static int id = 1;
	
	//产生唯一id
	private synchronized static int generateId() {
		return id++;
	}
	
	public static GuardedObject getGuardedObject(int id) {
		return boxes.remove(id);
	}
	
	public static GuardedObject createGuardedObject() {
		GuardedObject go = new GuardedObject(generateId());
		boxes.put(go.getId(), go);
		
		return go;
	}

	public static Set<Integer> getIds() {
		return boxes.keySet();
	}
	
}

class GuardedObject {
	private int id;
	
	public GuardedObject(int id) {
		this.id = id;
	}

	public int getId() {
		return id;
	}

	private Object response; // 结果

	//增加超时不等待效果
	//timeout：表示要等待多久
	public Object get(long timeout) {
		synchronized (this) {
			// 开始时间
			long begin = System.currentTimeMillis();
			// 经历的时间
			long passedTime = 0;
			// 没有结果
			while (response == null) {
				// 计算这一轮循环应该等待的时间
				long waitTime = timeout - passedTime;
				// 经历的时间超过了最大等待时间，退出循环，也就是剩余等待时间小于等于0
				// if(passedTime >= timeout)
				if(waitTime <= 0) break;
				try {
					this.wait(waitTime);	//虚假唤醒
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				// 计算经历的时间
				passedTime = System.currentTimeMillis() - begin;
			}
			return response;
		}
	}

	public void complete(Object response) {
		synchronized (this) {
			// 给结果成员变量赋值
			this.response = response;
			this.notifyAll();
		}
	}
}
